#ifndef __DALELIB_HALFEDGEMESH_H__
#define __DALELIB_HALFEDGEMESH_H__

#include <assert.h>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;

#include <dalelib/v2.h>
#include <dalelib/v3.h>
#include <dalelib/m4.h>

namespace dalelib
{

class HalfEdgeMesh
{
protected:

	struct Vertex
	{
		v3 _pos, _nrm;
		v2 _uv;
		int _edge; // point to one of its connected halfedges
		Vertex(v3 _Pos=v3(0,0,0),v3 _Nrm=v3(0,0,0),v2 _UV=v2(0,0)) : _pos(_Pos), _nrm(_Nrm), _uv(_UV) { }
	};

	struct HalfEdge
	{
		int _vert;
		int _next;
		int _twin;
		HalfEdge(int _Vert,int _Next,int _Twin) : _vert(_Vert), _next(_Next), _twin(_Twin) { }
	};

	typedef vector<Vertex> VertexList;
	VertexList _vertices;
	typedef vector<HalfEdge> HalfEdgeList;
	HalfEdgeList _edges;

public:

	Mesh() { }
	~Mesh() { }

	VertexList& getVertices() { return _vertices; }
	HalfEdgeList& getEdges() { return _edges; }

	void drawBadEdges()
	{
		glColor3f(0,1,0);
		glBegin(GL_LINES);
			for(unsigned int i=0; i<_edges.size(); ++i)
			{
				if(_edges[i]._twin==-1)
				{
					glVertex3fv(_vertices[_edges[i]._vert]._pos.ptr());
					glVertex3fv(_vertices[_edges[_edges[i]._next]._vert]._pos.ptr());
				}
			}
		glEnd();
	}


	void draw()
	{
/*
		glBegin(GL_TRIANGLES);
			for(unsigned int i=0; i<_edges.size(); ++i)
			{
				glNormal3fv(_vertices[_edges[i]._vert]._nrm.ptr());
				glTexCoord2fv(_vertices[_edges[i]._vert]._uv.ptr());
				glVertex3fv(_vertices[_edges[i]._vert]._pos.ptr());
			}
		glEnd();
/*/
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glColor3f(0,1,0);
		glBegin(GL_POINTS);
			for(unsigned int i=0; i<_vertices.size(); ++i)
			{
				glVertex3fv(_vertices[i]._pos.ptr());
			}
		glEnd();
//*/
drawBadEdges();
	}

	void transform(const m4 _Mat)
	{
		for(unsigned int i=0; i<_vertices.size(); ++i)
		{
			_vertices[i]._pos = _Mat*_vertices[i]._pos;
		}
	}

	void calcTwins()
	{
		int num = 0;
		for(unsigned int i=0; i<_edges.size(); ++i)
		{
			int v1 = _edges[i]._vert;
			int v2 = _edges[_edges[i]._next]._vert;
			bool found = false;
			for(unsigned int j=0; j<_edges.size() && !found; ++j)
			{
				if( (_edges[j]._vert==v2) && (_edges[_edges[j]._next]._vert==v1) )
				{
					_edges[i]._twin = j;
					found = true;
					num++;
				}
			}
		}
//		halt_assert(num==_edges.size(), "error finding twin edges in mesh");
	}

	void calcNormals()
	{
		for(unsigned int i=0; i<_vertices.size(); ++i) _vertices[i]._nrm = v3(0,0,0);
		for(unsigned int i=0; i<_edges.size(); )
		{
			v3 v0 = _vertices[_edges[i+1]._vert]._pos-_vertices[_edges[i]._vert]._pos;
			v3 v1 = _vertices[_edges[i+2]._vert]._pos-_vertices[_edges[i]._vert]._pos;
			v3 nrm = v1.cross(v0).unit();
			_vertices[_edges[i  ]._vert]._nrm += nrm;
			_vertices[_edges[i+1]._vert]._nrm += nrm;
			_vertices[_edges[i+2]._vert]._nrm += nrm;
			i += 3;
		}
		for(unsigned int i=0; i<_vertices.size(); ++i) _vertices[i]._nrm.normalise();
	}

	void heal()
	{

	}

};

} // namespace dalelib

#endif
